package game.Engine.Entity;

import game.MainController;
import game.Engine.Entity.Magic.MagicType;
import game.Input.EventSound;
import game.display.Animation;
import game.display.Images;

import java.awt.Image;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

public class Player extends Creature {
   public static final long JUMP_TIME = 100000000;
   private static final double JUMP_SPEED = -8f;
   private static final double ACCELERATION = 12f;
   private static final double DECELERATION = -4f;

   public boolean crouched;
   private transient boolean acceleratingLeft;
   private transient boolean acceleratingRight;
   private transient boolean doubleJump;
   private transient boolean doubleJumpAlready;
   private transient int totalPoints;

   private transient Animation nonRepeatingAnimation;
   private boolean[] playerMagic = { true, false, false, false, false };

   private static Animation[] standing, jumpingRight, jumpingLeft, jumpingStill,
         slidingLeft, slidingRight, slidingStill, movingLeft1, movingLeft2,
         movingLeft3, movingLeft4, movingRight1, movingRight2, movingRight3,
         movingRight4, dead;
   static {
      standing = new Animation[] { Images.PLAYERSTANDING.image(),
            Images.PLAYERSTANDING_RED.image(),
            Images.PLAYERSTANDING_GREEN.image(),
            Images.PLAYERSTANDING_BLUE.image(),
            Images.PLAYERSTANDING_YELLOW.image() };
      jumpingRight = new Animation[] { Images.PLAYERJUMPINGRIGHT.image(),
            Images.PLAYERJUMPINGRIGHT_RED.image(),
            Images.PLAYERJUMPINGRIGHT_GREEN.image(),
            Images.PLAYERJUMPINGRIGHT_BLUE.image(),
            Images.PLAYERJUMPINGRIGHT_YELLOW.image() };
      jumpingLeft = new Animation[] { Images.PLAYERJUMPINGLEFT.image(),
            Images.PLAYERJUMPINGLEFT_RED.image(),
            Images.PLAYERJUMPINGLEFT_GREEN.image(),
            Images.PLAYERJUMPINGLEFT_BLUE.image(),
            Images.PLAYERJUMPINGLEFT_YELLOW.image() };
      jumpingStill = new Animation[] { Images.PLAYERJUMPINGSTILL.image(),
            Images.PLAYERJUMPINGSTILL_RED.image(),
            Images.PLAYERJUMPINGSTILL_GREEN.image(),
            Images.PLAYERJUMPINGSTILL_BLUE.image(),
            Images.PLAYERJUMPINGSTILL_YELLOW.image() };
      slidingLeft = new Animation[] { Images.PLAYERSLIDINGLEFT.image(),
            Images.PLAYERSLIDINGLEFT_RED.image(),
            Images.PLAYERSLIDINGLEFT_GREEN.image(),
            Images.PLAYERSLIDINGLEFT_BLUE.image(),
            Images.PLAYERSLIDINGLEFT_YELLOW.image() };
      slidingRight = new Animation[] { Images.PLAYERSLIDINGRIGHT.image(),
            Images.PLAYERSLIDINGRIGHT_RED.image(),
            Images.PLAYERSLIDINGRIGHT_GREEN.image(),
            Images.PLAYERSLIDINGRIGHT_BLUE.image(),
            Images.PLAYERSLIDINGRIGHT_YELLOW.image() };
      slidingStill = new Animation[] { Images.PLAYERSLIDINGSTILL.image(),
            Images.PLAYERSLIDINGSTILL_RED.image(),
            Images.PLAYERSLIDINGSTILL_GREEN.image(),
            Images.PLAYERSLIDINGSTILL_BLUE.image(),
            Images.PLAYERSLIDINGSTILL_YELLOW.image() };
      movingLeft1 = new Animation[] { Images.PLAYERMOVINGLEFT1.image(),
            Images.PLAYERMOVINGLEFT1_RED.image(),
            Images.PLAYERMOVINGLEFT1_GREEN.image(),
            Images.PLAYERMOVINGLEFT1_BLUE.image(),
            Images.PLAYERMOVINGLEFT1_YELLOW.image() };
      movingLeft2 = new Animation[] { Images.PLAYERMOVINGLEFT2.image(),
            Images.PLAYERMOVINGLEFT2_RED.image(),
            Images.PLAYERMOVINGLEFT2_GREEN.image(),
            Images.PLAYERMOVINGLEFT2_BLUE.image(),
            Images.PLAYERMOVINGLEFT2_YELLOW.image() };
      movingLeft3 = new Animation[] { Images.PLAYERMOVINGLEFT3.image(),
            Images.PLAYERMOVINGLEFT3_RED.image(),
            Images.PLAYERMOVINGLEFT3_GREEN.image(),
            Images.PLAYERMOVINGLEFT3_BLUE.image(),
            Images.PLAYERMOVINGLEFT3_YELLOW.image() };
      movingLeft4 = new Animation[] { Images.PLAYERMOVINGLEFT4.image(),
            Images.PLAYERMOVINGLEFT4_RED.image(),
            Images.PLAYERMOVINGLEFT4_GREEN.image(),
            Images.PLAYERMOVINGLEFT4_BLUE.image(),
            Images.PLAYERMOVINGLEFT4_YELLOW.image() };
      movingRight1 = new Animation[] { Images.PLAYERMOVINGRIGHT1.image(),
            Images.PLAYERMOVINGRIGHT1_RED.image(),
            Images.PLAYERMOVINGRIGHT1_GREEN.image(),
            Images.PLAYERMOVINGRIGHT1_BLUE.image(),
            Images.PLAYERMOVINGRIGHT1_YELLOW.image() };
      movingRight2 = new Animation[] { Images.PLAYERMOVINGRIGHT2.image(),
            Images.PLAYERMOVINGRIGHT2_RED.image(),
            Images.PLAYERMOVINGRIGHT2_GREEN.image(),
            Images.PLAYERMOVINGRIGHT2_BLUE.image(),
            Images.PLAYERMOVINGRIGHT2_YELLOW.image() };
      movingRight3 = new Animation[] { Images.PLAYERMOVINGRIGHT3.image(),
            Images.PLAYERMOVINGRIGHT3_RED.image(),
            Images.PLAYERMOVINGRIGHT3_GREEN.image(),
            Images.PLAYERMOVINGRIGHT3_BLUE.image(),
            Images.PLAYERMOVINGRIGHT3_YELLOW.image() };
      movingRight4 = new Animation[] { Images.PLAYERMOVINGRIGHT4.image(),
            Images.PLAYERMOVINGRIGHT4_RED.image(),
            Images.PLAYERMOVINGRIGHT4_GREEN.image(),
            Images.PLAYERMOVINGRIGHT4_BLUE.image(),
            Images.PLAYERMOVINGRIGHT4_YELLOW.image() };
      dead = new Animation[] { Images.PLAYERDEAD.image(),
            Images.PLAYERDEAD_RED.image(), Images.PLAYERDEAD_GREEN.image(),
            Images.PLAYERDEAD_BLUE.image(), Images.PLAYERDEAD_YELLOW.image() };
   }

   private MagicType type;
   private long[] magictimers = { 0, 0, 0, 0, 0 };
   private transient double fireRate;
   private boolean jumping = false;
   private long jumpIn = 0;
   public int ID;
   public Player(Point2D.Double p, int ID) {
      super((double) p.getX(), (double) p.getY(), Images.PLAYERSTANDING.image());
      me = p;
      this.ID = ID;
      this.totalPoints = 0;
      type = MagicType.AIR;
      this.state = State.NORMAL;
      if (MainController.name.equals("JJ")) {
         maxHealth = 20000;
         health = 20000;
         fireRate = 8;
      } else {
         maxHealth = 200;
         health = 200;
         fireRate = 1;
      }
      crouched = false;
      setOnGround(true);
   }

   public int getTotalPoints() {
      return totalPoints;
   }

   public void addPoints(int t) {
      this.totalPoints += t;
   }

   public Rectangle2D.Double getBoundingBox() {
      if (boundingBox == null) {
         boundingBox = new Rectangle2D.Double(x, y, 1, 1);
      }
      boundingBox.x = x;
      boundingBox.y = y;
      if (crouched) {
         boundingBox.y += .5;
         boundingBox.height = .5;
      } else {
         boundingBox.height = 1;
      }
      return boundingBox;
   }

   public long getMagicTime(int magicIndex) {
      if ((magicIndex >= 0) && (magicIndex < magictimers.length)) {
         return magictimers[magicIndex];
      }
      return Long.MIN_VALUE;
   }

   public long getMaxMagicTime(int magicIndex) {
      if ((magicIndex >= 0) && (magicIndex < magictimers.length)) {
         return magicCooldowns[magicIndex];
      }
      return Long.MIN_VALUE;
   }

   public void changePlayerMagicUp() {
      if (this.getState() != State.DYING) {
         while (true) {
            type = type.getNextType();
            if (playerMagic[type.getNum()])
               break;
         }
      }
   }

   private transient Point2D.Double me;

   public Point2D.Double getTarget() {
      me.setLocation(x, y);
      return me;
   }

   public void changePlayerMagicDown() {
      if (this.getState() != State.DYING) {
         while (true) {
            type = type.getLastType();
            if (playerMagic[type.getNum()])
               break;
         }
      }
   }

   public void setWhite() {
      if (playerMagic[MagicType.AIR.getNum()])
         type = MagicType.AIR;
   }

   public void setRed() {
      if (playerMagic[MagicType.FIRE.getNum()])
         type = MagicType.FIRE;
   }

   public void setBlue() {
      if (playerMagic[MagicType.WATER.getNum()])
         type = MagicType.WATER;
   }

   public void setGreen() {
      if (playerMagic[MagicType.EARTH.getNum()])
         type = MagicType.EARTH;
   }

   public void setYellow() {
      if (playerMagic[MagicType.LIGHTNING.getNum()])
         type = MagicType.LIGHTNING;
   }

   public void slide() {
      crouched = true;
   }

   public void standUp() {
      crouched = false;
   }

   /**
    * Makes the player jump if the player is on the ground or if doubleJump is
    * true.
    */
   public void setAcceleratingLeft(boolean t) {
      acceleratingLeft = t;
   }

   public void setAcceleratingRight(boolean t) {
      acceleratingRight = t;
   }

   public void jump(boolean doubleJump) {
      if (!doubleJumpAlready) {
         if ((isOnGround() || (doubleJump && this.doubleJump)) && (!jumping)) {
            jumping = true;
            jumpIn = JUMP_TIME;
            if (dx == 0)
               nonRepeatingAnimation = jumpingStill[type.getNum()].clone();
            else if (dx > 0)
               nonRepeatingAnimation = jumpingRight[type.getNum()].clone();
            else
               nonRepeatingAnimation = jumpingLeft[type.getNum()].clone();
         }
         if (doubleJump && this.doubleJump) {
            doubleJumpAlready = true;
            executeJump();
         }
      }
   }

   private void executeJump() {
      setOnGround(false);
      if (!crouched) {
         if (getVelocityY() > 0) {
            setVelocityY(getVelocityY() + JUMP_SPEED);
         } else {
            setVelocityY(JUMP_SPEED);
         }
      } else {
         if (getVelocityY() > 0) {
            setVelocityY(getVelocityY() + JUMP_SPEED * 1.25);
         } else {
            setVelocityY(JUMP_SPEED * 1.25);
         }
      }
      jumping = false;
   }

   public void setY(double y) {
      super.setY(y);
   }

   public double getMaxSpeed() {
      return 0.5;
   }

   @Override
   public void fall() {
      setOnGround(false);
      jumping = false;
   }

   public void updateImage() {
      if ((nonRepeatingAnimation != null) && (!nonRepeatingAnimation.isDone())) {
         gi = nonRepeatingAnimation;
      } else {
         nonRepeatingAnimation = null;
         int cycle = Math.abs((int) (dx / 4.0));
         if (!crouched) {
            if (getVelocityX() == 0)
               gi = standing[type.getNum()];
            else if (getVelocityX() > 0) {
               if (cycle == 0)
                  gi = movingRight1[type.getNum()];
               else if (cycle == 1)
                  gi = movingRight2[type.getNum()];
               else if (cycle == 2)
                  gi = movingRight3[type.getNum()];
               else
                  gi = movingRight4[type.getNum()];
            } else {
               if (cycle == 0)
                  gi = movingLeft1[type.getNum()];
               else if (cycle == 1)
                  gi = movingLeft2[type.getNum()];
               else if (cycle == 2)
                  gi = movingLeft3[type.getNum()];
               else
                  gi = movingLeft4[type.getNum()];
            }
         } else {
            if (getVelocityX() == 0)
               gi = slidingStill[type.getNum()];
            else if (getVelocityX() > 0)
               gi = slidingRight[type.getNum()];
            else
               gi = slidingLeft[type.getNum()];
         }
      }
   }

   private void checkForOtherCollisions() {
      for (Entity e : MainController.gameEngine
            .runQuery(MainController.gameEngine.getPowerUpsAround(this))) {
         if (collidesWith(e)) {
            handleCollision(e);
            e.handleCollision(this);
         }
      }
   }

   public void handleCollision(Entity e) {
      if (e instanceof PowerUp) {
         MainController.soundController.addEventSoundAction(EventSound.POWERUP);
         PowerUp p = (PowerUp) e;
         if (p.getType() == PowerUp.Type.EARTH)
            playerMagic[MagicType.EARTH.getNum()] = true;
         else if (p.getType() == PowerUp.Type.FIRE)
            playerMagic[MagicType.FIRE.getNum()] = true;
         else if (p.getType() == PowerUp.Type.WATER)
            playerMagic[MagicType.WATER.getNum()] = true;
         else if (p.getType() == PowerUp.Type.LIGHTNING)
            playerMagic[MagicType.LIGHTNING.getNum()] = true;
         else if (p.getType() == PowerUp.Type.DOUBLEJUMPPOWER)
            doubleJump = true;
         else if (p.getType() == PowerUp.Type.HEALTH)
            fullHeal();
      }
   }

   public void die() {
      for (int j = 0; j < playerMagic.length; j++) {
         playerMagic[j] = false;
      }
      super.die();
   }

   public void update(long timeElapsed) {
      super.update(timeElapsed);
      if (jumping) {
         jumpIn -= timeElapsed;
         if (jumpIn <= 0) {
            executeJump();
         }
      }
      if (!isOnGround()) {
         setVelocityY(getVelocityY() + GRAVITY * timeElapsed / 1000000000);
      }
      if (acceleratingLeft) {
         if ((!crouched) || (!isOnGround()))
            setVelocityX(getVelocityX() - ACCELERATION * timeElapsed * fireRate
                  / 1000000000);
      }
      if (acceleratingRight) {
         if ((!crouched) || (!isOnGround()))
            setVelocityX(getVelocityX() + ACCELERATION * timeElapsed * fireRate
                  / 1000000000);
      }
      if (state == State.NORMAL) {
         if (isOnGround()) {
            doubleJumpAlready = false;
            if (!crouched) {
               if (getVelocityX() == 0) {
               } else if (getVelocityX() > 0) {
                  setVelocityX(getVelocityX() + DECELERATION * timeElapsed
                        / 1000000000);
               } else {
                  setVelocityX(getVelocityX() - DECELERATION * timeElapsed
                        / 1000000000);
               }
            } else {
               if (getVelocityX() == 0) {
               } else if (getVelocityX() > 0) {
                  setVelocityX(getVelocityX() + DECELERATION * timeElapsed
                        / 6000000000L);
               } else {
                  setVelocityX(getVelocityX() - DECELERATION * timeElapsed
                        / 6000000000L);
               }
            }
         }
         checkForOtherCollisions();
         updateTimers(timeElapsed);
      }
   }
   private void updateTimers(long timeElapsed) {
      for (int j = 0; j < magictimers.length; j++) {
         magictimers[j] -= timeElapsed;
         if (magictimers[j] < 0) {
            magictimers[j] = 0;
         }
      }

   }

   public void cast() {
      if (state != State.NORMAL)
         return;
      if (magictimers[type.getNum()] <= 0) {
         magictimers[type.getNum()] = (long)(magicCooldowns[type.getNum()]/fireRate);
         me.setLocation(x, y);
         MainController.gameEngine.entities.add(new Magic(me, 10,
               MainController.gameEngine.mouse.getTarget(), type, this,
               getVelocityX(), getVelocityY()));
      }
   }

   public boolean getPlayerMagic(int i) {
      return playerMagic[i];
   }
   
   public boolean[] getPlayerMagicArray(){
	   return playerMagic;
   }

   public int getCurrentPowerUpSelected() {
      return type.getNum();
   }

   @Override
   public void hitBy(Magic m) {
      double modifier = getModifier(m.getType());
      hit(modifier * BASE_DAMAGE);
   }

   public Animation getDyingImage() {
      return dead[type.getNum()];
   }

   @Override
   public void setOnGround(boolean onGround) {
      super.setOnGround(onGround);
      if (onGround) {
         if (getVelocityY() > 15) {
            hit(getVelocityY() * 1.25);
         }
      }
   }
   
   public MagicType getType(){
	   return type;
   }
   
   public void matchWith(Player p){
		z = p.getZ();
		x = p.getX();
		y = p.getY();
		dx = p.getVelocityX();
		dy = p.getVelocityY();
		state = p.getState();
		health = p.getHealth();
		maxHealth = p.getMaxHealth();
		crouched = p.isCrouched();
		playerMagic = p.getPlayerMagicArray();
		type = p.getType();
		magictimers = p.getMagicTimer();
		jumping = p.isJumping();
		jumpIn = p.getJumpIn();
		ID = p.getID();
   }
   
   public long getJumpIn(){
	   return jumpIn;
   }
   
   public int getID(){
	   return ID; 
   }
   
   public boolean isJumping(){
	   return jumping;
   }
   
   public long[] getMagicTimer(){
	   return magictimers;
   }
   
   public boolean isCrouched(){
	   return crouched;
   }

   private double getModifier(MagicType type2) {
      if (type2 == MagicType.LIGHTNING) {
         return 4;
      }
      switch (type) {
      case LIGHTNING:
         return 2;
      case AIR:
         return 1;
      case WATER:
         switch (type2) {
         case FIRE:
            return .5;
         case EARTH:
            return 1.5;
         case WATER:
            return 1;
         case AIR:
            return 1;
         case LIGHTNING:
            return 4;
         }
      case FIRE:
         switch (type2) {
         case FIRE:
            return 1;
         case EARTH:
            return .5;
         case WATER:
            return 1.5;
         case AIR:
            return 1;
         case LIGHTNING:
            return 4;
         }
      case EARTH:
         switch (type2) {
         case FIRE:
            return 1.5;
         case EARTH:
            return 1;
         case WATER:
            return .5;
         case AIR:
            return 1;
         case LIGHTNING:
            return 4;
         }
      }
      return 1;
   }
}
